package com.cosmo.integration.web;

import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.springframework.beans.factory.FactoryBean;

public class HttpClientFactoryBean implements FactoryBean {

	private final DefaultHttpClient httpClient;

	public HttpClientFactoryBean(String username, String password, String host, int port) {

		// new way to do things in http4 - see http://hc.apache.org/httpcomponents-client/examples.html
		// also the wiki has a tutorial posted - http://wiki.apache.org/HttpComponents/HttpClientTutorial
		// or the api - http://hc.apache.org/httpcomponents-client/httpclient/apidocs/index.html
		// and core - http://hc.apache.org/httpcomponents-core/httpcore/apidocs/index.html

		// SCHEME
		SchemeRegistry supportedSchemes = new SchemeRegistry();
		supportedSchemes.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), port));

		// DEFAULTS
		HttpParams params = new BasicHttpParams();
		HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, "UTF-8");

        // set to true can help speed up 'post' and 'put' by not sending the body until the server accepts the request
        // so, when authentication is required its useful, but prob not if we used pre-emptive
        // currently wait-for-continue waits 3 seconds for this 'ok to send body' response
        // we now have pre-emptive auth, so disable this. possibly 
        // http://hc.apache.org/httpcomponents-core/httpcore/apidocs/org/apache/http/params/CoreProtocolPNames.html#USE_EXPECT_CONTINUE
        //HttpProtocolParams.setUseExpectContinue(params, true);

        // change time to wait to see if expect-continue is given
        // httpget.getParams().setParameter(CoreProtocolPNames.WAIT_FOR_CONTINUE, (1 sec, defaults to 3));
		// realistic figures from tutorial
		// ConnManagerPNames.MAX_TOTAL_CONNECTIONS = 20
		// ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE = 2
        ConnManagerParams.setMaxTotalConnections(params, 200);
        ConnPerRouteBean maxRoutes = new ConnPerRouteBean(20);
        maxRoutes.setMaxForRoute(new HttpRoute(new HttpHost("localhost")), 50);
        ConnManagerParams.setMaxConnectionsPerRoute(params, maxRoutes);

        // TODO localhost probably doesn't not need to worry about these...
        // TODO is it worth a gzip interceptor (see example, maybe tutorial)
        // TODO is it worth a keep alive strategy (see tutorial)

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, supportedSchemes);
		httpClient = new DefaultHttpClient(ccm, params);

		// administrator credentials are provided which are able to access all users
		// "An administrator may access all users' USDs." - http://chandlerproject.org/Projects/CosmoUserServiceDocument
		httpClient.getCredentialsProvider().setCredentials(
                new AuthScope(host, port, AuthScope.ANY_REALM), 
                new UsernamePasswordCredentials(username, password));

        // its worth a preemptiveAuth
		// see the interceptor for more details
        //BasicHttpContext localContext = new BasicHttpContext();
        //BasicScheme basicAuth = new BasicScheme();
        //localContext.setAttribute("preemptive-auth", basicAuth);
        httpClient.addRequestInterceptor(new HttpClientPreemptiveAuthenticationInterceptor(), 0);
	}

	public Object getObject() throws Exception {
		return httpClient;
		/* httpclient 3.1
		HttpClient httpClient = new DefaultHttpClient();
		Credentials defaultcreds = new UsernamePasswordCredentials(username, password);
		httpClient.getState().setCredentials(new AuthScope(host, port, AuthScope.ANY_REALM), defaultcreds);
		*/
	}

	public Class getObjectType() {
		return HttpClient.class;
	}

	public boolean isSingleton() {
		return true;
	}

	public void destroy() {
		if (httpClient != null)
			httpClient.getConnectionManager().shutdown();
	}

}
